{-# LANGUAGE TemplateHaskell, PatternSignatures #-}
module EmailTest where

import qualified Data.Map as M
import Data.Binary (encodeFile, decodeFile, decode, get, put, Binary)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LB
import Data.Derive.Binary
import Data.DeriveTH (derive)
import Control.Monad 
import Data.Accessor.Template (deriveAccessors)
import Data.Accessor ((^.))


--data Date = Day Integer | Month Integer | Year Integer

data Address = At String String
data Subject = Subject String
data Body = Body String
data MessageID = Mid String
   deriving Show
data UniqueMessageID = UniqueMessageID Integer
   deriving Show

-- Uit deze datatypes wordt een email uit opgebouwd.

type Word = String
type BodyIndex = M.Map Word [UniqueMessageID]

-- Voor ons logische benamingen voor woorden en voor de index die we gaan maken van de bodies van al onze emails.

fromBody :: Body -> String
fromBody (Body x) = x

-- Deze functie gebruiken we om met de string te kunnen werken die de body eigenlijk is.

data Email = Email
  {
    address_ :: Address
  , subject_ :: Subject
--, date_ :: Date
  , body_ :: Body
  , mid_ :: MessageID
  }

-- Hier bouwen we onze email daadwerkelijk op. We kunnen de volgende syntax gebruiken:
{- Email a b c d
Email { address = ...,  }
em { address = ... }
address :: 
-}


data NumberedEmail = NEmail
  {
    email_ :: Email
  , umid_ :: UniqueMessageID 
  }

{-Unfortunately, the messageID that is send with an email is not entirely unique, so I have to generate a Unique ID to use in my index. I do this using a new datatype NumberedEmail, which has the extra structure of a unique message ID. -}


$( deriveAccessors ''Email )
$( deriveAccessors ''NumberedEmail )
$( fmap concat $ mapM (derive makeBinary) [''Email,{-''Date,-}''Address,''Subject,''Body, ''MessageID, ''UniqueMessageID] )

sdecodeFile :: (Binary a) => String -> IO a
sdecodeFile s = fmap (decode . LB.fromChunks . return) $ B.readFile s

{- Hierboven staan een aantal technische details: Template Haskell om goed met Email en NumberedEmail te kunnen werken, en een strikte versie van decodeFile om te zorgen dat herhaaldelijk encoden en decoden van een file binnen een programma geen problemen geeft -}


giveUniqueMessageID :: Email -> IO NumberedEmail
giveUniqueMessageID e = do
   ( getal :: Integer ) <- fmap (+ 1) ( sdecodeFile "IDlist" )
   encodeFile "highestID" getal
   return (NEmail e ( UniqueMessageID getal ))

{- Hier geven we onze email een UniqueMessageID voor gebruik in de index. Het wordt dan een numberedemail. -}


updateBodyIndex :: NumberedEmail -> BodyIndex -> BodyIndex
updateBodyIndex x index = M.unionWith (++) index (M.fromList . fmap (\ y -> (y, [x ^. umid])) . words . fromBody . (^. body) . (^. email ) $ x) 

{- Deze functie gaan we gebruiken als er een nieuwe email binnenkomt; die zal de email indexeren en deze index combineren met de index die we al hebben van al onze andere emails -}

saveEmail :: Email -> IO()
saveEmail e = encodeFile ((\ (Mid x) -> x) (e ^. mid )) e

readEmail :: MessageID -> IO String  
readEmail (Mid m) = fmap (fromBody . (^. body)) $ decodeFile m

saveIndex :: BodyIndex -> IO()
saveIndex = encodeFile "indexfile"

readIndex :: IO BodyIndex
readIndex = sdecodeFile "indexfile"

{- Met deze functies slaan we emails en de index op als bestanden en lezen we ze ook weer terug -}